package ch.uzh.ifi.ddis.ds2011.a3;

import java.util.Map;
import java.util.Random;

import ch.uzh.ifi.ddis.ds2011.a3.helper.PixelMap;

import com.signalcollect.ExecutionInformation;
import com.signalcollect.Graph;
import com.signalcollect.configuration.ExecutionMode;
import com.signalcollect.javaapi.ExecutionConfiguration;
import com.signalcollect.javaapi.GraphBuilder;
import com.signalcollect.javaapi.IdStateJavaAggregator;

public class SchellingsSegregation {

	public int number_of_iterations; 					// Maximal number of
	private int columns; 								// Number of columns in the simulation grid
	private int rows; 									// Number of rows in the simulation grid
	private float equality_threshold;
	
	private boolean[] initial_state;
	
	private PixelMap image = new PixelMap("Schelling's seggregation");
	private Graph g;

	public SchellingsSegregation(int rows, int columns, int number_of_iterations, float equality_threshold, boolean[] initial_state){
		this.rows = rows;
		this.columns = columns;
		this.number_of_iterations = number_of_iterations;
		this.equality_threshold = equality_threshold;
		this.initial_state = initial_state;
		
		// THIS WILL INITIALIZE THE PROCESSING GRAPH
		this.init();
	}
	
	public void init() {
		g = GraphBuilder.withNumberOfWorkers(1).build();

		// initialize vertices from initial_state
		//TODO: add vertexes here !

		// initialize edges from initial_state
		//TODO: add the edges here!
	}

	public void executionStep(boolean showImage) {
		// this is a helper method, designed to start a GUI and printing of running statistics in the console.
		ExecutionInformation stats = g.execute(ExecutionConfiguration
				.withExecutionMode(ExecutionMode.Synchronous()).withStepsLimit(
						1));
		if (showImage) {
			byte[] data = new byte[this.rows * this.columns];
			Map<Integer, Boolean> idValueMap = g
					.aggregate(new IdStateJavaAggregator<Integer, Boolean>());
			for (int id : idValueMap.keySet()) {
				if (idValueMap.get(id)) {
					data[id] = (byte) 1;
				} else {
					data[id] = (byte) 0;
				}
			}
			image.setData(data);
			while (image.isNotUpdated()) {
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		System.out.println(stats);
	}

	public void shutDown() {
		g.shutdown();
	}


	/**
	 * utility method to convert grid coordinates to ids. Id starts with 0 in
	 * the top left corner and increments row wise.
	 * 
	 * if x1<x2 then generateVertexId(x1, y) < generateVertexId(x2, y) if y1<y2
	 * then generateVertexId(x, y1) < generateVertexId(x, y2)
	 * 
	 * @param row
	 * @param collumn
	 * @return the unique id for the cell at the specified location
	 */
	private int generateVertexId(int row, int collumn) {
		return row * this.columns + collumn;
	}

	/**
	 * Utility that checks if a coordinate is within the grid. All negative
	 * values and all values bigger or equal to the size constraints of the grid
	 * are considered invalid.
	 * 
	 * @param row
	 * @param collumn
	 * @return
	 */
	private boolean isValidCoordinate(int row, int collumn) {
		if (row < 0 || row >= this.rows) {
			return false;
		} else if (collumn < 0 || collumn >= this.columns) {
			return false;
		}
		return true;
	}

	/**
	 * This method creates the initial state randomly
	 * 
	 * @return
	 */
	private static boolean[] randomSeed(int rows, int columns) {
		Random rand = new Random();
		boolean[] seed = new boolean[rows * columns];
		for (int i = 0; i < seed.length; i++) {
			seed[i] = rand.nextBoolean();
		}
		return seed;
	}
	
	private static boolean[] getSeed(String data) {
		String[] vals= data.split(",");
		boolean[] seed = new boolean[vals.length];
		for (int i = 0; i < seed.length; i++) {
			seed[i] = Boolean.valueOf(vals[i]);
		}
		return seed;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		int number_of_iterations = 50; 					// Maximal number of
		int columns = 40; 								// Number of columns in the simulation grid
		int rows = 30; 									// Number of rows in the simulation grid
		float equality_threshold = 0.50f;
		boolean[] initial_state;
		
		
		if (args.length >= 4) {
			rows = Integer.valueOf(args[0]);
			columns = Integer.valueOf(args[1]);
			number_of_iterations = Integer.valueOf(args[2]);
			equality_threshold = Float.valueOf(args[3]);
		}
		
		if (args.length == 5) {
			initial_state = SchellingsSegregation.getSeed(args[4]);
		} else {
			initial_state = SchellingsSegregation.randomSeed(rows, columns);
		}
		
		SchellingsSegregation simulation = new SchellingsSegregation(rows, columns, number_of_iterations, equality_threshold, initial_state);
		for (int i = 0; i < simulation.number_of_iterations; i++) {
			simulation.executionStep(true);
		}
		simulation.shutDown();

	}

}
